package lhy.orm.dao;

import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lhy.AppStart;
import lhy.exception.ForeignKeyException;
import lhy.orm.OrmUtils;
import lhy.orm.UniLoader;
import lhy.orm.annotation.Table;
import lhy.orm.model.EntityData;
import lhy.orm.model.ForeignKey;
import lhy.lhyorm.dao.Dao;
import lhy.lhyorm.dao.DaoManager;
import lhy.utils.ReflectUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


public class GenericDaoImpl<T,ID> implements GenericDao<T>{
    public static final String ALL_COLUMNS="*";
    private final Class<T> entityClass;
    private final String entityTableName;
    private final Dao<T, ID> DAO ;
    public GenericDaoImpl(Class<T> entityClass) {
        this.entityClass = entityClass;
        Table anno = entityClass.getAnnotation(Table.class);
		if(anno!=null) {
            this.entityTableName =anno.value();
        }else{
            this.entityTableName = entityClass.getSimpleName();
        }

        try {
            DAO = DaoManager.createDao(DatabaseConnector.getConnectionSource(), entityClass);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public List<T> queryForAll(){
        try {
            return DAO.queryForAll();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    //condition :   表名首字母小写_id = 值
    public List<T> queryByCondition(String condition) {
        String[] split = condition.split("=");
        String col = split[0];
        String val = split[1];
        try {
            return DAO.queryBuilder().where().eq(col, val).query();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public int create(Object obj){
        try {
            return DAO.create((T) obj);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public int update(Object obj){
        try {
            return DAO.update((T) obj);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public int delete(Object id){
        try {
            return DAO.deleteById((ID) id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public List<T> get(String columns, String where) throws SQLException {

        final EntityData entityData = UniLoader.entityNameClassMap.get(ReflectUtils.getClassFullName(entityClass));
        String sql = String.format("select %s from %s %s",columns,entityData.getEntityTableName(),where);
        AppStart.LOGGER.info("执行SQL：{}",sql);
        ResultSet rs = DatabaseConnector.getConnection().prepareStatement(sql).executeQuery();

        List<T> objectRowList = new ObjectArrayList<>();
        //如果是基础实体，直接获取
        if (entityData.isBasicBean()) {
            while(rs.next()){
                T obj = ReflectUtils.newInstance(entityClass);
                Arrays.stream(ReflectUtils.getAccessibleFields(entityClass)).forEachOrdered(field -> {
                    ReflectUtils.setFieldValueFromResultSet(field,obj,rs);
                });
                objectRowList.add(obj);
            }

        }
        //如果不是基础实体
        else{
            //存储 字段名/外键对象 vs 值 的map

            //循环 每一行数据
            while (rs.next()){

                //创建实体实例
                T entityObj = ReflectUtils.newInstance(entityClass);
                //创建Map： 数据库字段名(String/ForeignKey)vs值
                Map<Serializable,Object> fieldNameValueMap = new Object2ObjectArrayMap<>();
                //遍历每一个列，把值存储在map中
               /* Arrays.stream(ReflectUtils.getAccessibleFields(entityClass)).forEachOrdered(field -> {
                        ForeignKey fk = OrmUtils.isFieldHasForeignKey(field);
                        //没有外键，map直接放 字段名 vs 值
                        if(fk ==null) {
                            try {
                                fieldNameValueMap.put(field.getName(),rs.getObject(field.getName()));
                            } catch (SQLException e) {
                                throw new RuntimeException(e);
                            }
                        }
                        //有外键，往map里放 外键对象 和 本对象的数据库cell值
                        else{
                            try {
                                String srcColumn = fk.getSourceColumn();
                                fieldNameValueMap.put(fk,rs.getObject(srcColumn));
                            } catch (SQLException e) {
                                throw new RuntimeException(e);
                            }
                        }
                });*/
                fieldNameValueMap.forEach((fieldName,value)->{
                    if (fieldName instanceof String fns) {
                        //字段本身
                        Field fieldByName = ReflectUtils.getFieldByName(entityClass, fns);
                        //给字段赋值
                        ReflectUtils.setFieldValue(fieldByName, entityObj, value);
                    }else if(fieldName instanceof ForeignKey fk){
                        try {
                            loadForeignKey(entityObj,fk,value);
                        } catch (SQLException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
                objectRowList.add(entityObj);


            }

        }


        return objectRowList;
    }
    private Object loadForeignKey(Object entityObj,ForeignKey fk,Object fieldValue) throws SQLException {
        ResultSet rs2 = DatabaseConnector.preparedStatement(String.format("select * from %s where %s='%s'", fk.getTargetTable(), fk.getTargetColumn(), fieldValue)).executeQuery();
        if(!rs2.next()) return entityObj;
        Class<?> fkClz = ReflectUtils.loadClassByName(fk.getTargetClass());

        Object fkIns = ReflectUtils.newInstance(fkClz);
        Class<?> fkSrcClz = ReflectUtils.loadClassByName(fk.getSourceClass());
        Object fkSrcIns = ReflectUtils.newInstance(fkSrcClz);
        List<Field> accessibleFieldsList = ReflectUtils.getAccessibleFieldsList(fkClz);
        for (Field field : accessibleFieldsList) {
            ForeignKey fk2 = null;//OrmUtils.isFieldHasForeignKey(field);
            if(fk2==null){
                Object rs2Object = rs2.getObject(field.getName());
                AppStart.LOGGER.info("外键——设置字段 {} 的值为 {}", field.getName(), rs2Object);
                ReflectUtils.setFieldValue(field, fkIns, rs2Object);

            }else{
                AppStart.LOGGER.info("外键2——设置字段 {}", field.getName());
                Object fkInsideValue = rs2.getObject(fk2.getSourceColumn());
                Class<?> fkInsideClazz = ReflectUtils.loadClassByName(fk2.getTargetClass());
                Object fkInsideInstance = ReflectUtils.newInstance(fkInsideClazz);
                entityObj= loadForeignKey(fkInsideInstance,fk2,fkInsideValue);
                fk=fk2;
            }
            /*
            if (fk2 != null) {

                ResultSet rs22 = DatabaseConnection.preparedStatement(String.format("select * from %s where %s='%s'", fk2.getTargetTable(), fk2.getTargetColumn(), fk2Val)).executeQuery();



                if (rs22.next()) {
                    ReflectUtils.getAccessibleFieldsList(fkClz2).forEach(field1 -> {
                        try {
                            Object rs22Object = rs22.getObject(field1.getName());
                            ReflectUtils.setFieldValue(field1, fk2Ins, rs22Object);
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }

                    });
                }
            } else*/
        }
        //字段本身
        Field fieldByName;
        try {
            fieldByName = ReflectUtils.getFieldByName(entityClass, fk.getOriginalFieldName());
        } catch (ForeignKeyException e) {
            fieldByName = ReflectUtils.getFieldByName(ReflectUtils.loadClassByName(fk.getSourceClass()), fk.getOriginalFieldName());
        }
        boolean subFk=false;
        //给字段赋值
        try {
            ReflectUtils.setFieldValue(fieldByName, entityObj, fkIns);
        } catch (IllegalArgumentException e) {
            subFk=true;
            try {
                ReflectUtils.setFieldValue(fieldByName, fkSrcIns, fkIns);
            } catch (IllegalArgumentException ex) {
                try {
                    ReflectUtils.setFieldValue(fieldByName, fkIns,entityObj);
                } catch (IllegalArgumentException exc) {
                    try {
                        entityObj=ReflectUtils.combine2Objects(fkIns,entityObj);
                        AppStart.LOGGER.info(entityObj);
                        subFk=false;
                    } catch (InstantiationException instantiationException) {
                        throw new RuntimeException(instantiationException);
                    } catch (IllegalAccessException illegalAccessException) {
                        throw new RuntimeException(illegalAccessException);
                    } catch (InvocationTargetException invocationTargetException) {
                        throw new RuntimeException(invocationTargetException);
                    }
                }
            }
        }

        return subFk?fkSrcIns:entityObj;
    }
    @Override
    public List<T> getAll(){
        try {
            return getBy("");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<T> getBy(String whereClause) throws SQLException {
        return getByFrom(whereClause, entityTableName);
    }

    @Override
    public List<T> getByFrom(String whereClause, String anotherTableName) throws SQLException {
        return getColumnsByFrom(whereClause,anotherTableName,ALL_COLUMNS);
    }

    @Override
    public List<T> getColumnsByFrom(String whereClause, String anotherTableName, String columnsNeed) throws SQLException {
        ArrayList lists = new ArrayList<>();
        String sql=String.format(
                "select %s from %s %s", columnsNeed,anotherTableName, whereClause
        );
        AppStart.LOGGER.info("\n"+sql);
        ResultSet rs = DatabaseConnector.getConnection().prepareStatement(sql).executeQuery();
        T modelIns;
        while (rs.next()) {
            //创建新实例
            modelIns = ReflectUtils.newInstance(entityClass);
            //获取全部字段
            final T finalModelIns = modelIns;
            ReflectUtils.traversalEveryField(entityClass, field ->ReflectUtils.setFieldValueFromResultSet(field,finalModelIns,rs));
            /*for (Field field : modelClass.getDeclaredFields()) {
                field.setAccessible(true);
                field.set(modelIns, rs.getObject(ReflectUtils.getFieldName(field)));
            }*/
            lists.add(modelIns);
        }

        return lists;
    }


    @Override
    public int insert(T modelInstance) throws SQLException {
        int fieldAmount = ReflectUtils.getAccessibleFields(entityClass).length;
        StringBuilder sb = new StringBuilder("INSERT INTO " + entityTableName + "  VALUES (");
        for (int i = 0; i < fieldAmount; i++) {
            sb.append("?");
            //结尾不应该有逗号，所以判断减1
            if (i != fieldAmount - 1)
                sb.append(",");

        }
        sb.append(")");
        String sql = sb.toString();
        AppStart.LOGGER.info(sql);
        PreparedStatement pstm  = DatabaseConnector.getConnection().prepareStatement(sql);
        final int[] i = {0};
        ReflectUtils.traversalEveryField(entityClass, field -> {
            try {
                String key = field.getName();
                Object value = field.get(modelInstance);
                //如果有外键
                /*if(getForeignKey(field)!=null){
                    String sqlcond=String.format("select %s from %s where %s=?",
                            getForeignKeyColumnName(field),
                            getForeignKeyTableName(field),
                            getForeignKeyColumnName(field) );
                    AppStart.LOGGER.info(sqlcond);
                    PreparedStatement ps2=DatabaseConnection.getConnection().prepareStatement(sqlcond);
                    ps2.setObject(1,value);
                    ResultSet rs=ps2.executeQuery();
                    if(!rs.next()) {
                        throw new SQLException();
                        return;
                    }
                }*/


                pstm.setObject(i[0] + 1, value);
                ++i[0];
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
        return pstm.executeUpdate();
    }

    @Override
    public int update(Object id, T modelInstance) throws SQLException {
        StringBuilder sb = new StringBuilder("UPDATE " + entityTableName + " SET ");
        //全部变量名
        Field[] fields = ReflectUtils.getAccessibleFields(entityClass);
        //变量的数量
        int fieldAmount = fields.length;
        for (int i = 0; i < fieldAmount; i++) {
            Field fn=fields[i];
            fn.setAccessible(true);
            sb.append(fn.getName()+" = ?");
            //结尾不应该有逗号，所以判断减1
            if (i != fieldAmount - 1)
                sb.append(",");

        }
        sb.append(" WHERE id=?");
        String sql=sb.toString();
        AppStart.LOGGER.info("更新的SQL语句是"+sql);
        PreparedStatement ps = DatabaseConnector.getConnection().prepareStatement(sql);
        int i;
        Object idVal=null;
        boolean isIdValAutoIncrement = false;
        for ( i = 0; i < fieldAmount; i++) {
            Field fn = fields[i];
            fn.setAccessible(true);
            if(OrmUtils.isFieldIdentity(fn)){
                if(OrmUtils.isFieldAutoIncrement(fn)){
                    idVal = null;
                    isIdValAutoIncrement=true;
                }else{
                    idVal = ReflectUtils.getFieldValue(fn,modelInstance);
                }
            }
            Object value = ReflectUtils.getFieldValue(fn,modelInstance);
            ps.setObject(i+1,value);
            AppStart.LOGGER.info("设置SQL语句中第"+(i+1)+"个占位符的值为"+value);
        }
        //最后一个where id=?的
        ps.setObject(fieldAmount+1,id==null?idVal:id);
        AppStart.LOGGER.info("设置更新的id为"+(id==null?idVal:id));
        return ps.executeUpdate();
    }



   /* public int delete(String idField,Object id) throws SQLException{
    	StringBuilder sb=new StringBuilder("DELETE FROM "+ entityTableName +" WHERE "+idField+"=?");
        String sql=sb.toString();
        AppStart.LOGGER.info(sql+id);
        PreparedStatement ps = DatabaseConnector.getConnection().prepareStatement(sql);
        ps.setObject(1,id);
        return ps.executeUpdate();
    }*/

}
